Introducing Antares (a Wayland window manager implemented as a library)
History
My first ever "real" programming project was a window manager for X11.
It sucked a lot. I had never written any C before, so I did not
understand what I was actually doing. Neither did I know where to look
things up. I literally hadn't even heard of poll()
yet, didn't even
know how to compile a multi-file C program without just #include
'ing
all source files, let alone how to architect such a program - or any
slightly complex program.
But I pushed through regardless. Because it was fun! More specifically, the fun part was experimenting with window management policy. The fun part was creating custom UI elements. I could ignore the pain of juggling all the atoms because playing around with the actual policy was fun!
You'd think Xlib was written by chemists, based on how every slightly involved action requires it's own molecule of atoms…
Eventually I realized that not just my WM sucked, but also X in general. I tried sway and immediately knew that this was something good. Something nice. I sunset my WM and over time wrote various software for Wayland.
I am not interested in discussing the virtues of X. I've seen all arguments you could possibly come up with already; Please don't waste your and my time by repeating them.
I also tried creating my own Wayland server, using wlroots. I got reasonanly far, but eventually drowned in a million small things. This was beyond my skill level at the time, not because I couldn't do those things, but rather because I didn't know how to properly design a program encompassing them all.
Then I got "adopted" by the river project. I was asking about
something on the #wlroots
channel and enountered Isaac. He was also
in the process of writing a Wayland server using wlroots and he was
just a tiny slither farther along than I was. About one or two days of
work probably. Inspite of me being a bit weirded out by that weird new
programming language he used, somehow he convinced me to join his
project; Probably because I saw that we had roughly similar goals and
that I could just contribute the optional features I would like to
have.
At first I contributed a few window layouts. Later the ability to call an external executable to generate layouts, which we eventually turned into a proper protocol to have layouts generated by custom Wayland clients.
This did not see much usage outside of the reference layout generator shipped with river, my own experiments and a few from the wider community. In retrospect this makes sense, since the layout protocol ultimately is still very limiting: Regardless of much though you put into your window layout, it will always be a dwm clone.
Introducing the WM protocol
River in its current state is very usable. However both Isaac and I think it could be better and both of us want to experiment with window management again.
Instead of hardcoding those experiments in river, it was decided to replace the layout protocol with a full-blown window management protocol. For some time I actually thought that wasn't viable, but experience with the layout protocol showed me otherwise.
Introducing Antares
Writing a window manager for river using the window-management protocol is a lot simpler than writing a window manager for X. And I have gained a lot of experience since I first hacked on my X WM. I could easiely write a window manager suitable for my purposes in less than a week. Arguably I have now probably also gained the necessary skill and competence to write my own server entirely.
But what I want to do is not just write and use a thing, I want to experiment, tweak as I go. I want to build new and interesting things, iterate designs quickly, not get lost in the implementation details. I am very comfortable writing Wayland clients in C, but here I want something more malleable (I don't like the term "high-level").
For all its strengths, Wayland depends on a lot of auto-generated code. This makes it painful to use directly from languages were you can't just statically link that generated code into a compiled binary.
And the language I want to use, Guile Scheme, does not even have a native Wayland implementatin or bindings for libwayland, so I would have to call into the generated code via guiles FFI. That would be immensely painful, also because some of the lower Wayland plumbing and boilerplate is better expressed in C than in a Lisp I find.
Since I don't have the time or patience to write a Wayland client implementation for guile, there is no way around the FFI.
Guile Scheme is a weird language. I don't mean because it's a lisp, I find lisps to be quite reasonable. I mean it in the way, that it calls itself a scripting language and ships with powerful libraries, but at the same time lacks a lot of higher level control flow sugar you'd expect from a scripting language and somehow allows you to write very low-level and very efficient code.
So what if there was a simple library that did most of the lower plumbing internally and exposed a simple API which wouldn't be too painful to use through an FFI?
And that's basically what antares is.
Antares is intentioned to allow you to experiment with window management in a carefree manner: Boilerplate, server connection, error handling and more are neatly abstracted away without limiting what you can do.
The core is a C library which does all the annoying things plus some minor quality of life. The outer layer are language specific bindings; As of writing, antares ships bindings for guile scheme, more may be added later. And finally a reasonably well featured window manager written in scheme.
A potential user could hack on the shipped reference window manager, write their own in scheme, or use the C library directly.
Discoveries
I already played around quite a lot with antares. In fact it's fair to say that the scheme reference window manager has grown quite a bit beyond what one may expect from a mere reference program.
One of my favourite discoveries is focus-or-hide-or-run
. It's an
upgrade to the typical "run or raise" feature some may know: Triggered
by a keybind, try to focus a window and if it doesn't exist, launch
that program. I've added the "hide" feature to it: If the bind is
triggered when the window is already focused, hide it. This small
addition increases its usefulness quite a lot. I can work in one
window, quickly raise another window - regardless of whether that
program is running already - and just as quickly dismiss that window
again and return to the previous window with very little mental
overhead. I have defined such a keybind for emacs, firefox, a
terminal and IRC.
Before antares, I used tiling window management for years and made
heavy use of workspaces. I wanted to challange myself by creating a
non-tiling window manager without workspaces which is at least as
efficient and comfortable for me than my previous setup. The
focus-or-hide-or-run
feature is basically half of that.
Limitations and Final Thoughts
There are some limitations with regards to what kinds of window
management you can create with antares: No matter what you do, you
will always position windows on a two-dimensional plane which
intersects the plane of the output configuration. If that is too
limiting for what you have in mind, you'll likely have to write your
own display server. These limitations are imposed by the
river-window-management-v1
protocol rather than by antares itself,
which in turn is likely wlroots' scene graph API leaking into the
design.
Regardless, I am convinced that even within these limitations, interesting things are possible. If I may ask only one thing of you, a potential user of the library, that would be that you be creative and do something different. The world has enough dwm clones already.
– Thank you for reading –